using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
using T.FAS.Runtime.Action.Common;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Action.SPI;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using static T.FAS.Runtime.Metadata.Entity.DataModel.StateMachineRefSchemaEntity;
using T.FAS.Runtime.Expression.Service;
using T.FAS.Runtime.Expression.Entity;

namespace T.FAS.Runtime.Action.Engine
{
    public class SGActionEngine : IActionExecuteProxy, IActionDataResultBuilder, IActionExecuteHandler
    {
        public void AfterExecute(ActionExecContext execContext)
        {
            
        }

        public void BeforeExecute(ActionExecContext execContext)
        {
            
        }

        public TResult BuildActionDataResult(SPI.ActionExecContext context, object dataResult)
        {
            var actionMetaData = ActionRTPUtil.ActionMetadataValid(context.MetaData);
            if (actionMetaData.ReturnValueType == Metadata.Entity.ReturnValueType.Void)
                return null;
            if (actionMetaData.Method == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExec_MissingMethodDefinition, actionMetaData.Code));
            var outputResult = actionMetaData.MethodParams.Find(x => x.ParamType == ParamType.Out);
            if (outputResult == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExec_MissingMethodRetuanValueTypeDefinition, actionMetaData.Code));
            if (outputResult.ParamSourceType == ParamSourceType.Basic)
            {
                var result = new TCommonResult<TRecord>()
                {
                    ActionCode = actionMetaData.Code,
                    ActionName = actionMetaData.Name,
                    Count = 0,
                    ResultType = actionMetaData.ReturnValueType == ReturnValueType.Record ? DataType.Record : DataType.List,
                    DataContent = dataResult,
                };
                result.Schema = BasicTypeData.BuildSchema(outputResult.ParamValueType);
                var isList = actionMetaData.ReturnValueType == Metadata.Entity.ReturnValueType.List ? true : false;
                var isUserDefineResult = UserDefineResultHandler.ConvertToTResultByMappingJS(dataResult, context , result);
                if (!isUserDefineResult)
                {
                    if (result.ResultType == DataType.Record)
                    {
                        result.Record = new TRecord();
                        result.Record.SetValue(BasicTypeData.DefaultPropertyName, dataResult);
                    }
                    else
                    {
                        TResponseUtil.ConvertTResponseToTResult(Convert.ToString(dataResult), actionMetaData.ReturnValueType == Metadata.Entity.ReturnValueType.List ? true : false, result, BasicTypeData.BuildSchema(outputResult.ParamValueType));
                    }
                }
                return result;
            }
            else if (outputResult.ParamSourceType == ParamSourceType.CDO ||
                outputResult.ParamSourceType == ParamSourceType.DO ||
                outputResult.ParamSourceType == ParamSourceType.DQ ||
                outputResult.ParamSourceType == ParamSourceType.EO ||
                outputResult.ParamSourceType == ParamSourceType.SDO)
            {

                var isList = actionMetaData.ReturnValueType == Metadata.Entity.ReturnValueType.List ? true : false;
                var tResult = new TCommonResult<JObject>()
                {
                    ActionCode = actionMetaData.Code,
                    ActionName = actionMetaData.Name,
                    Count = 0,
                    ResultType = isList ? DataType.List : DataType.Record,
                    EntityType = TEntityType.SGEO,
                    Record = new JObject()
                };
                EntityObject entityObject = null;
                if (actionMetaData.MethodParams != null && actionMetaData.MethodParams.Count > 0)
                {
                    var outParam = actionMetaData.MethodParams.Find(x => x.ParamType == ParamType.Out);
                    if (outParam != null)
                    {
                        entityObject = new T.FAS.Runtime.DataModel.Plugin.EntityObjectManager().GetMetaDataContent(outParam.SourceID) as EntityObject;
                    }
                }
                if (entityObject == null)
                    throw new Exception(CommonStringClassEngine.SGActionEngine_BuildActionDataResult2);
                tResult.Schema = entityObject.BuildSchema();
                var isUserDefineResult = UserDefineResultHandler.ConvertToTResultByMappingJS(dataResult, context, tResult);
                 if (!isUserDefineResult)
                {                    
                    TResponseUtil.ConvertTResponseToTResult(Convert.ToString(dataResult), actionMetaData.ReturnValueType == Metadata.Entity.ReturnValueType.List ? true : false, tResult, tResult.Schema);
                }
                return tResult;
            }
            return null;
        }

        public object Execute(ActionExecContext context)
        {
            var action = ActionRTPUtil.ActionMetadataValid(context.MetaData);
            var sid = action.Method.Code;
            var paramList = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            if (action.MethodParams != null && action.MethodParams.Count > 0)
            {
                var inParamsAndVariable = action.MethodParams.FindAll(x=>x.ParamType == Metadata.Entity.ParamType.In || x.ParamType == ParamType.Variable);
                if (inParamsAndVariable != null && inParamsAndVariable.Count > 0)
                {
                    if (!string.IsNullOrEmpty(action.ParamMappingJS))
                    {
                        paramList = ActionParamMappingHandler.BuildParamsByMapping(context);
                        //将更新了变量值的Action刷新到上下文中的Action元数据中
                        context.MetaData = action;

                    }
                    else
                    {
                        BuildParamsNormal(context, paramList, inParamsAndVariable);
                    }
                }
            }
            if (action.Method.IsInvokeLimit && !paramList.ContainsKey(ActionRTPConst.DevAppIDKey) && !string.IsNullOrEmpty(context.AppID))
                paramList[ActionRTPConst.DevAppIDKey] = context.AppID;
            var service = new SGInvoker();
            var result = service.Post(sid,paramList, context.Cookies);
            if (result.state.Equals("0"))
            {
                throw new FasException(ActionExecuteExceptionCode.SGActionBusinessLogicError, result.errmsg);
            }
            else
                return result.data;
        }

        
        public void ExecuteError(ActionExecContext execContext, Exception exception)
        {
            
        }
        
        public object ExecuteWithSubEngine(IActionExecuteEngine subEngine, ActionExecContext context)
        {
            throw new NotImplementedException();
        }
        private void BuildParamsNormal(ActionExecContext context, Dictionary<string, object> paramList, List<ActionMetaDataMethodParam> inParams)
        {
            inParams.ForEach(p =>
            {
                if (!context.DataContent.ContainsKey(p.Code) && p.DefaultValue == null)
                {
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteParamNullError, string.Format(CommonStringClassEngine.ActionExec_ParameterIsNotAssigned, p.Code));
                }
                if (context.DataContent.ContainsKey(p.Code))
                {
                    paramList[p.Code] = context.DataContent[p.Code];
                }
                else
                {
                    paramList[p.Code] = p.DefaultValue;
                }
            });
        }
    }
}
